const fs = require('fs')
const path = require('path')
const os = require('os')
const { exec, execSync } = require('child_process')
const { error } = require('./lib/stdan.js')
const express = require('express')
const app = express()

app.use(express.static(path.resolve(__dirname, 'public')))
app.use(express.urlencoded({ extended: true }))

let options
if (fs.existsSync(path.join(__dirname, 'options.json'))) 
    options = require('./options.json')
else {
    options = {
        "videoBitRate": "8",
        "maxFps": "off",
        "maxSize": "off",
        "displayOrientation": "off",
        "noDisplay": "off",
        "alwaysOnTop": "off",
        "stayAwake": "off",
        "turnScreenOff": "off",
        "noAudio": "off",
        "noVideo": "off",
        "fullScreen": "off",
        "windowBorderless": "off",
        "recordPath": `${path.join(os.homedir(), 'Desktop')}`
    }
    fs.writeFileSync(path.join(__dirname, 'options.json'), JSON.stringify(options), 'utf8')
}

const optionsMap = {
    videoBitRate: '--video-bit-rate',
    maxFps: '--max-fps',
    maxSize: '--max-size',
    displayOrientation: '--display-orientation',
    noDisplay: '--no-display',
    alwaysOnTop: '--always-on-top',
    stayAwake: '--stay-awake',
    turnScreenOff: '--turn-screen-off',
    noAudio: '--no-audio',
    noVideo: '--no-video',
    fullScreen: '--fullscreen',
    windowBorderless: '--window-borderless',
    recordPath: '--record'
}

/**
 * getDeviceList 获取设备列表
 * @param {Function} 回调函数, 参数: (data)
 * @returns {void}
*/
function getDeviceList(callback) {
    exec(`${__dirname}/cli/adb.exe devices`, (err, data) => {
        let res = { devices: [] }
        if (err) {
            error(err.message)
            callback(res)
            return
        }
        data = Array.from(new Set(data.replace('List of devices attached\r\n', '').replace(/\tdevice/g, '\t已连接').replace(/\tunauthorized/g, '\t未授权').replace(/\toffline/g, '\t离线').replace(/\r\n\r\n/g, '').split('\r\n')))
        if (data.length === 1)
            if (data[0] === '' || data[0] === '\r\n') {
                callback(res)
                return
            }
        data.forEach(e => {
            let item = e.split('\t')
            let model
            try {
                model = execSync((item[1] === '离线' || item[1] === '未授权') ? `echo unknown` : `${__dirname}/cli/adb.exe -s ${item[0]} shell getprop ro.product.model`).toString().replace('unknown', '❓未知').replace(/\r\n/g, '')
            } catch (err) {
                model = '❓未知'
                item[1] = '无法连接'
            }
            res.devices.push({ model, id: item[0], status: item[1] })
        })
        callback(res)
    })
}

/**
 * getThisDateTime 获取当前日期和时间
 * @returns {String} 日期时间格式化字符串
*/
function getThisDateTime() {
    let date = new Date
    function padZero(num) {
        return num < 10 ? '0' + num : num;
    }
    return (
        date.getFullYear() +
        '-' + padZero(date.getMonth() + 1) +
        '-' + padZero(date.getDate()) +
        '-' + padZero(date.getHours()) +
        '-' + padZero(date.getMinutes()) +
        '-' + padZero(date.getSeconds())
    )
}

/**
 * keyOptToCliOpt key表示的选项转换为命令行中的选项
 * @param {Object} options keys选项
 * @param {Object} maps 选项映射
 * @param {String} model 设备型号
 * @param {Boolean} isRecord 是否开启屏幕录制
 * @returns {String} cli选项
*/
function keyOptToCliOpt(options, maps, model = '未知', isRecord = false) {
    let cliOpts = []
    for (let key in options) {
        if (options[key] === 'on') {
            if (key === 'noDisplay') {
                if (isRecord) 
                    cliOpts.push(maps[key])
            } else 
                cliOpts.push(maps[key])
        } else if (options[key] === 'off')
            continue
        else if (key === 'recordPath') {
            if (isRecord) {
                let value = `${options[key]}/${model}-${getThisDateTime()}.mp4`
                cliOpts.push(maps[key], value)
            }
        } else {
            if (key === 'videoBitRate') 
                cliOpts.push(maps[key], options[key] + 'M')
            else 
                cliOpts.push(maps[key], options[key])
        }
    }
    cliOpts.push('--video-codec', 'h265', '--audio-codec', 'opus')
    return cliOpts.join(' ')
}

/**
 * updateOptions 更新选项
 * @param {String} key 选项的key
 * @param {String} value 选项的value
 * @returns {void}
*/
function updateOptions(key, value) {
    options[key] = value
    try {
        fs.writeFileSync(
            path.join(__dirname, 'options.json'),
            JSON.stringify(options), 'utf8'
        )
        return {status: true, err: null}
    } catch (err) {
        error(err.message)
        return {status: false, err: err.message}
    }
}

/**
 * startScrcpy 启动Scrcpy
 * @param {String} deviceId 设备ID
 * @param {Array} options scrcpy选项
 * @param {String} model 设备型号
 * @param {Boolean} isRecord 是否开启屏幕录制
 * @param {Function} callback 回调函数, 参数 (data)
 * @returns {void}
*/
function startScrcpy(deviceId, options, model, isRecord, callback) {
    isRecord = isRecord === 'true' ? true : false
    let cliOpts = keyOptToCliOpt(options, optionsMap, model, isRecord)
    console.log(cliOpts);
    exec(`${__dirname}/cli/scrcpy.exe -s ${deviceId} ${cliOpts}`, (err) => {
        if (err) {
            let regex = /ERROR: .*/g
            callback(err.message.match(regex).join(' - ').replace(/ERROR: /g, ''))
        }
    })
}

app.get('/api/getDevices', (req, res) => {
    getDeviceList(data => {
        res.json(data)
    })
})

app.get('/api/getOptions', (req, res) => {
    res.json(options)
})

app.post('/api/updateOption', (req, res) => {
    res.json(
        updateOptions(
            req.body['key'],
            req.body['value']
        )
    )
})

app.post('/api/startScrcpy', (req, res) => {
    startScrcpy(req.body.deviceId, options, req.body.model, req.body.isRecord, err => {
        res.send(err)
    })
    res.send('ok')
})

app.listen('5352', '127.0.0.1', () => {
    console.log('server is running http://localhost:5500')
})